package ee122Networking;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Timestamp;

public class TwoHopPacket {
	
	public static final int MY_PACKET_LENGTH = 128; 
	public static final int MY_PACKET_HEADER_LENGTH = 28;
	public static final int MY_PACKET_DATA_LENGTH = MY_PACKET_LENGTH - MY_PACKET_HEADER_LENGTH;
	
	public static final int MY_PACKET_SEQUENCE_NUMBER_OFFSET = 0;
	public static final int MY_PACKET_DEST_PORT_OFFSET = 4;
	public static final int MY_PACKET_TIMESTAMP_OFFSET = 8;
	public static final int MY_PACKET_SRC_ADDR_OFFSET = 16;
	public static final int MY_PACKET_SRC_PORT_OFFSET = 20;
	public static final int MY_PACKET_DATA_OFFSET = MY_PACKET_HEADER_LENGTH;
	public static final int MY_PACKET_TOTAL_PACKETS_OFFSET = 24;
	private byte[] twoHopPacket;
	
	private byte[] data;
	private int sequenceNumber;
	private int sourcePort;
	private int destinationPort;
	private byte[] sourceAddressByte;
	private String sourceAddressStr;
	private long timeStamp;
	private int totalPackets;
	
	public TwoHopPacket(int seqNum){
		this.twoHopPacket = new byte[MY_PACKET_LENGTH];

		this.setSequenceNumber(seqNum);
		
		
	}
	
	
	public TwoHopPacket(byte[] udpPacketData) throws IOException {
		twoHopPacket = udpPacketData;
		
		// get sequenceNumber
		byte[] seqnoData = new byte[4];
		System.arraycopy(udpPacketData, MY_PACKET_SEQUENCE_NUMBER_OFFSET, seqnoData, 0, 4);
		ByteArrayInputStream bis = new ByteArrayInputStream(seqnoData);
		DataInputStream dis = new DataInputStream(bis);
		sequenceNumber = dis.readInt();
		
		// get sourcePort
		byte[] sourcePortData = new byte[4];
		System.arraycopy(udpPacketData, MY_PACKET_SRC_PORT_OFFSET, sourcePortData, 0, 4);
		bis = new ByteArrayInputStream(sourcePortData);
		dis = new DataInputStream(bis);
		sourcePort = dis.readInt();
		
		// get destinationPort
		byte[] destPortData = new byte[4];
		System.arraycopy(udpPacketData, MY_PACKET_DEST_PORT_OFFSET, destPortData, 0, 4);
		bis = new ByteArrayInputStream(destPortData);
		dis = new DataInputStream(bis);
		destinationPort = dis.readInt();
		
		// get sourceAddressByte
		sourceAddressByte = new byte[4];
		System.arraycopy(udpPacketData, MY_PACKET_SRC_ADDR_OFFSET, sourceAddressByte, 0, 4);
		
		// get sourceAddressStr
		
		sourceAddressStr = InetAddress.getByAddress(sourceAddressByte).toString();
		
		// get timestamp
		byte[] timestampData = new byte[8];
		System.arraycopy(udpPacketData, MY_PACKET_TIMESTAMP_OFFSET, timestampData, 0, 8);
		bis = new ByteArrayInputStream(timestampData);
		dis = new DataInputStream(bis);
		timeStamp = dis.readLong();

		// get totalPackets

		byte[] totalPacketsData = new byte[4];
        System.arraycopy(udpPacketData, MY_PACKET_TOTAL_PACKETS_OFFSET, totalPacketsData, 0, 4);
        bis = new ByteArrayInputStream(totalPacketsData);
        dis = new DataInputStream(bis);
        totalPackets = dis.readInt();

		
		// get data
		byte[] data = new byte[MY_PACKET_DATA_LENGTH];
		System.arraycopy(udpPacketData, MY_PACKET_DATA_OFFSET, data, 0, MY_PACKET_DATA_LENGTH);
	}
	
	public TwoHopPacket(int seqNum, int destPort, DatagramSocket socket, int tp ){
		this.twoHopPacket = new byte[MY_PACKET_LENGTH];
		
		this.setSequenceNumber(seqNum);
		this.setDestinationPort(destPort);
		this.setSourceAddress(socket);
		this.setSourcePort(socket);
		this.setTimeStamp();
		this.setData();
		this.setTotalPackets(tp);
	}
	
	public TwoHopPacket(int seqNum, int destPort, DatagramSocket socket, byte[] data, int tp ){
		this(seqNum, destPort, socket, tp);
		this.setData(data);
		
		
	}
	
	public int getDataLength(){
		return MY_PACKET_DATA_LENGTH;
	}
	public int getTotalPackets(){
		return totalPackets;
	}
	public byte[] getPacketByte(){
		return twoHopPacket;
	}
	
	public void setData(byte[] data){
		this.data = data;
		System.arraycopy(data, 0, this.twoHopPacket, MY_PACKET_DATA_OFFSET, MY_PACKET_DATA_LENGTH);		
	}
	
	public void setData(){
		this.data = new byte[MY_PACKET_DATA_LENGTH];
		
	}
	
	public byte[] getData(){
		return data;
	}
	
	public void setSequenceNumber(int sequenceNumber) {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bos);
		try {
			dos.writeInt(sequenceNumber);
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		byte[] seqnoData = bos.toByteArray();
		System.arraycopy(seqnoData, 0, twoHopPacket, MY_PACKET_SEQUENCE_NUMBER_OFFSET, 4);
		
		this.sequenceNumber = sequenceNumber;
	}

	public int getSequenceNumber() {
		return sequenceNumber;
	}

	
	public void setSourcePort(DatagramSocket socket) {
		this.sourcePort = socket.getLocalPort();
		
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bos);
		
		try {
			dos.writeInt(sourcePort);
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		byte[] srcPortdata = bos.toByteArray();
				
		System.arraycopy(srcPortdata, 0, twoHopPacket, MY_PACKET_SRC_PORT_OFFSET, 4);
		
	}
	
	public void setSourcePort(int srcPort) {
		this.sourcePort = srcPort;
		
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bos);
		
		try {
			dos.writeInt(sourcePort);
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		byte[] srcPortdata = bos.toByteArray();
				
		System.arraycopy(srcPortdata, 0, twoHopPacket, MY_PACKET_SRC_PORT_OFFSET, 4);
		
	}

	public int getSourcePort() {
		return sourcePort;
	}

	public void setDestinationPort(int destinationPort) {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bos);
		try {
			dos.writeInt(destinationPort);
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		byte[] destPortdata = bos.toByteArray();
		System.arraycopy(destPortdata, 0, twoHopPacket, MY_PACKET_DEST_PORT_OFFSET, 4);
		
	}
	
	public void setTotalPackets(int totalPackets) {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bos);
		try {
			dos.writeInt(totalPackets);
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		byte[] tpbyte = bos.toByteArray();
		System.arraycopy(tpbyte, 0, twoHopPacket, MY_PACKET_TOTAL_PACKETS_OFFSET, 4);
		
		this.totalPackets = totalPackets;
	}
	

	public int getDestinationPort() {
		return destinationPort;
	}

	public void setSourceAddress(DatagramSocket socket) {
		
		try {
			System.arraycopy(InetAddress.getLocalHost().getAddress(), 0, twoHopPacket, MY_PACKET_SRC_ADDR_OFFSET, 4);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	
	
	public byte[] getSourceAddressByte() {
		byte[] returnByteArray = new byte[4];
		System.arraycopy(sourceAddressByte, 0, returnByteArray, 0, 4);
		return returnByteArray;
	}
	
	public String getSourceAddressStr() {
		return sourceAddressStr;
	}
	

	public void setTimeStamp() {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bos);
		
		Long timestamp = System.currentTimeMillis();
		
		bos = new ByteArrayOutputStream();
		dos = new DataOutputStream(bos);
		try {
			dos.writeLong(timestamp);
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		byte[] timestampdata = bos.toByteArray();
		
		System.arraycopy(timestampdata, 0, twoHopPacket, MY_PACKET_TIMESTAMP_OFFSET, 8);
		
		this.timeStamp = timestamp;
	}

	public long getTimeStamp() {
		return timeStamp;
	}

	@Override
	public boolean equals(Object obj) {
		
		return (this.sequenceNumber == ((TwoHopPacket)obj).sequenceNumber);
		
		
		
	}
	
	

	
	
	
}
